package main;

import game.Game;

import org.eclipse.swt.*;
import org.eclipse.swt.awt.*;
import org.eclipse.swt.layout.*;
import org.eclipse.swt.widgets.*;
import org.eclipse.swt.events.*;

import javax.media.opengl.*;
import javax.media.opengl.glu.*;

import com.sun.opengl.util.FPSAnimator;
import com.sun.opengl.util.GLUT;

public class Main extends GLCanvas implements GLEventListener {

	private static final long serialVersionUID = 1L;

	private GLU glu;
	private GLUT glut;

	private int fps;
	public static FPSAnimator animator;

	private Game game;

	private Renderer renderer;

	public static void main(String[] args) {

		Display display = new Display();

		Main main = new Main();
		main.runMain(display);
		display.dispose();
	}

	void runMain(Display display) {
		final Shell shell = new Shell(display);
		shell.setText("Q*bert 3D - OpenGL Exercise");
		GridLayout gridLayout = new GridLayout();
		gridLayout.marginHeight = 0;
		gridLayout.marginWidth = 0;

		shell.setLayout(gridLayout);

		// this allows us to set particular properties for the GLCanvas
		GLCapabilities glCapabilities = new GLCapabilities();

		glCapabilities.setDoubleBuffered(true);
		glCapabilities.setHardwareAccelerated(true);

		// instantiate the canvas
		final GLCanvas canvas = new GLCanvas(glCapabilities);

		// we can't use the default Composite because using the AWT bridge
		// requires that it have the property of SWT.EMBEDDED
		Composite composite = new Composite(shell, SWT.EMBEDDED);
		GridData ld = new GridData(GridData.FILL_BOTH);
		composite.setLayoutData(ld);

		// the grid layout allows you to add more widgets in the main window.

		// set the internal layout so our canvas fills the whole control
		FillLayout clayout = new FillLayout();
		composite.setLayout(clayout);

		// create the special frame bridge to AWT
		java.awt.Frame glFrame = SWT_AWT.new_Frame(composite);
		// we need the listener so we get the GL events
		canvas.addGLEventListener(this);

		// finally, add our canvas as a child of the frame
		glFrame.add(canvas);

		composite.setFocus();
		canvas.requestFocus();

		// Start the animator
		fps = 60;
		animator = new FPSAnimator(canvas, fps);
		animator.start();

		composite.addKeyListener(new KeyAdapter() {
			@Override
			public void keyPressed(KeyEvent k) {
				game.handleKeyPressed(k);
				canvas.repaint();
			}

			public void keyReleased(KeyEvent k) {
				game.handleKeyReleased(k);
				canvas.repaint();
			}
		});

		// show it all
		shell.open();
		// the event loop.
		while (!shell.isDisposed()) {
			if (!display.readAndDispatch())
				display.sleep();
		}

	}

	@Override
	public void init(GLAutoDrawable drawable) {
		drawable.setGL(drawable.getGL());

		// Create the GLU
		final GL gl = drawable.getGL();
		glu = new GLU();
		glut = new GLUT();

		// Enable Z-Buffer
		gl.glEnable(GL.GL_DEPTH_TEST);
		gl.glDepthFunc(GL.GL_LEQUAL);
		gl.glClearDepth(1.0f);

		// Enable smooth shading
		gl.glEnable(GL.GL_LIGHTING);
		gl.glEnable(GL.GL_TEXTURE_2D);
		gl.glShadeModel(GL.GL_SMOOTH);

		// Define clear color
		gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

		gl.glHint(GL.GL_PERSPECTIVE_CORRECTION_HINT, GL.GL_NICEST);

		// Initialize the model view matrix
		gl.glMatrixMode(GL.GL_MODELVIEW);
		gl.glLoadIdentity();

		// Initialize the renderer and the scene
		renderer = new Renderer();
		game = new Game();
	}

	@Override
	public void display(GLAutoDrawable drawable) {
		if (!animator.isAnimating()) {
			return;
		}

		renderer.setEnvironment(drawable, glu, glut, drawable.getWidth(), drawable.getHeight());
		game.setEnvironment(drawable.getWidth(), drawable.getHeight());
		renderer.visit(game.updateScene());
	}

	@Override
	public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {
		GL gl = drawable.getGL();

		gl.glViewport(0, 0, width, height);
		gl.glMatrixMode(GL.GL_PROJECTION);
		gl.glLoadIdentity();

		glu.gluPerspective(45.0f, (double) width / (double) height, 0.1f, 1000.0f);
		gl.glMatrixMode(GL.GL_MODELVIEW);

	}

	@Override
	public void displayChanged(GLAutoDrawable arg0, boolean arg1, boolean arg2) {
		// should remain empty
	}

}
